package per.qiao.simplerest.components.resttemplate;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.HttpMessageConverterExtractor;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import per.qiao.simplerest.components.RemoteComponets;
import per.qiao.simplerest.restargument.RestArgumentResolver;
import per.qiao.simplerest.restargument.RestArgumentValue;
import per.qiao.simplerest.restargument.RestParameter;
import per.qiao.simplerest.restenum.RestMethod;
import per.qiao.simplerest.restresolver.RestAutoConfigBean;
import per.qiao.simplerest.restresolver.RestInvokerMethod;
import per.qiao.simplerest.restservice.ResponseDto;
import per.qiao.simplerest.returenvalue.DefaultValueHandler;
import per.qiao.simplerest.returenvalue.ReturnValueHandler;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Company: 上海数惠系统技术有限公司
 * Department: 数据中心
 * Date: 2020-05-31 0:48
 *
 * @Author: qyp
 * Email: qiaoyp@dist.com.cn
 * Desc:
 */
public abstract class RestTemplateComponents implements RemoteComponets {

    @Autowired
    RestAutoConfigBean restAutoConfigBean;

    @Autowired
    RestArgumentResolver restArgumentResolver;

    @Autowired(required = false)
    List<ReturnValueHandler> returnValueHandlers;

    @Autowired
    @Lazy
    private RestTemplate restTemplate;

    /**
     * 添加拦截器
     * @return
     */
    public List<ClientHttpRequestInterceptor> getClientHttpRequestInterceptor() {
        return new ArrayList<>();
    }

    @Override
    public Object invoke(RestInvokerMethod restInvokerMethod) {
        String uri = restInvokerMethod.getUri();
        RestMethod restMethod = restInvokerMethod.getRestMethod();
        Class returnType = restInvokerMethod.getReturnType();
        Type genericReturnType = restInvokerMethod.getGenericReturnType();

        String url = null;
        if (ObjectUtils.isEmpty(restInvokerMethod.getBase())) {
            url = restAutoConfigBean.getBase() + uri;
        }
        RestArgumentValue restArgumentValue = restArgumentResolver.resolver(restInvokerMethod);
        return doInvoke(restMethod, url, restArgumentValue, returnType, genericReturnType);
    }


    private <T> T doInvoke(RestMethod restMethod, String url, @Nullable RestArgumentValue restArgumentValue,
                           @Nullable Class<T> returnType, @Nullable Type genericReturnType) {

        // uri上的参数值
        Object[] uriVariables;
        Map<String, Object> requestParamValues;
        Object bodyValue = null;

        if (restArgumentValue == null) {
            uriVariables = new Object[0];
            requestParamValues = new HashMap<>(0);
        } else {
            uriVariables = restArgumentValue.getRestfulValues();
            requestParamValues = restArgumentValue.getRequestParamValues();
            bodyValue = restArgumentValue.getBodyValue();
        }

        // 把@RestParam这种注解的参数，拼接在路径上
        url = assembleUri(url, requestParamValues);

        T returnValue = null;
                ParameterizedTypeReference<T> objectParameterizedTypeReference = ParameterizedTypeReference.forType(genericReturnType);
        switch (restMethod) {

            case GET:
                ResponseEntity<T> result = restTemplate.exchange(url, HttpMethod.GET, null, objectParameterizedTypeReference, uriVariables);
                returnValue = result.getBody();
//                returnValue = restTemplate.getForObject(url, returnType, uriVariables);
                break;
            case POST:
                HttpEntity<?> httpEntity = new HttpEntity<>(bodyValue, null);
                result = restTemplate.exchange(url, HttpMethod.POST, httpEntity, objectParameterizedTypeReference, uriVariables);
                returnValue = result.getBody();
//                returnValue = restTemplate.postForObject(url, bodyValue, returnType, uriVariables);
//                return restTemplate.execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
                break;
            case PUT:
//                restTemplate.put(url, bodyValue, uriVariables);

                httpEntity = new HttpEntity<>(bodyValue, null);
                result = restTemplate.exchange(url, HttpMethod.PUT, httpEntity, objectParameterizedTypeReference, uriVariables);
                returnValue = result.getBody();
                /*RequestCallback requestCallback = restTemplate.httpEntityCallback(bodyValue, returnType);
                HttpMessageConverterExtractor<T> responseExtractor =
                        new HttpMessageConverterExtractor<>(returnType, restTemplate.getMessageConverters());
                returnValue = restTemplate.execute(url, HttpMethod.PUT, requestCallback, responseExtractor, uriVariables);*/
                break;
            case DELETE:
//                restTemplate.delete(url, uriVariables);
                httpEntity = new HttpEntity<>(bodyValue, null);
                result = restTemplate.exchange(url, HttpMethod.DELETE, httpEntity, objectParameterizedTypeReference, uriVariables);
                returnValue = result.getBody();

                /*requestCallback = restTemplate.httpEntityCallback(bodyValue, returnType);
                responseExtractor =
                        new HttpMessageConverterExtractor<>(returnType, restTemplate.getMessageConverters());
                returnValue = restTemplate.execute(url, HttpMethod.DELETE, requestCallback, responseExtractor, uriVariables);*/
            default:
                break;
        }
        /*for (ReturnValueHandler returnValueHandler : returnValueHandlers) {
            if (returnValueHandler.support(returnValue)) {
                return returnValueHandler.handle(returnValue, returnType, genericType);
            }
        }*/
        return returnValue;
    }

    /**
     * 组装uri
     * 把 protocol + port + ip + 项目名加上
     * @return
     */
    private String assembleUri(String uri, @Nullable Map<String, Object> params) {
        if (ObjectUtils.isEmpty(params)) {
            return uri;
        }
        StringBuilder sb = new StringBuilder();
        params.forEach((k, v) -> sb.append("&").append(k).append("=").append(v) );

        String paramSegment = sb.toString();
        uri = uri.endsWith("/") ? uri.substring(0, uri.length() - 2) : uri;

        if (StringUtils.hasText(paramSegment)) {
            return uri + "?" + paramSegment.substring(1);
        }
        return uri;
    }



    public abstract ClientHttpRequestFactory getClientHttpRequestFactory();
}
